# -*- coding: utf-8 -*-

class SuperLine(object):
    
    def __init__(self, initial='', tab_width=4):
        if initial == '':
            self.__string = self.string_class()
        else:
            self.__string = self.string_class(initial.splitlines(True)[0])
        self.tab_width = tab_width
    
    def __get_body(self):
        return self[len(self.indent):-len(self.ending)]
        
    def __set_body(self, body):
        self.__string = self.string_class(self.indent + body + self.ending)
        
    def __del_body(self):
        self.body = ''
        
    def __get_ending(self):
        return self[len(self.__string.rstrip('\r\n')):]
        
    def __set_ending(self, ending):
        if ending not in ['\r\n', '\r', '\n', '']:
            return
        self.__string = self.string_class(self[:-len(self.ending)] + ending)
        
    def __del_ending(self):
        self.ending = ''
        
    def __get_indent(self):
        return self[:-len(self.__string.lstrip(' \t'))]
        
    def __set_indent(self, indent):
        if indent.strip(' \t') != '':
            return
        self.__string = self.string_class(indent + self.body + self.ending)
        
    def __del_indent(self):
        self.indent = ''
    
    def __get_indent_type(self):
        indent_set = set(self.indent)
        if len(indent_set) == 1:
            return list(indent_set)[0]
        return indent_set
    
    def __set_indent_type(self, indent_type):
        if indent_type == ' ':
            self.indent = self.indent.expandtabs(self.tab_width)
        elif indent_type == '\t':
            self.indent = unexpand(self.indent, self.tab_width)
        return
    
    def __del_indent_type(self):
        self.indent_type = ' '
    
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.__string,)
    
    def __str__(self):
        return self.__string
    
    def __add__(self, string):
        return self.__class__(
            self.indent + self.body + string.rstrip('\r\n') + self.ending)
    
    def __contains__(self, substring):
        return self.body.__contains__(substring)
    
    def __eq__(self, obj):
        if isinstance(obj, SuperLine):
            return self.__string.__eq__(obj.string_class(obj))
        return self.__string.__eq__(obj)
    
    def __ge__(self, obj):
        return self.body.__ge__(obj)
    
    def __getitem__(self, index):
        return self.__string[index]
    
    def __getslice__(self, *args, **kwargs):
        return self.__string.__getslice__(*args, **kwargs)
    
    def __gt__(self, obj):
        return self.body.__gt__(obj)
    
    def __le__(self, obj):
        return self.body.__le__(obj)
    
    def __len__(self):
        return len(self.__string)
    
    def __lt__(self, obj):
        return self.body.__lt__(obj)
    
    def __mod__(self, subst):
        return self.__class__(self.indent + (self.body % subst) + self.ending)
    
    def __mul__(self, n):
        return self.__class__(self.indent + (self.body * n) + self.ending)
    
    def __ne__(self, obj):
        return self.__string.__ne__(obj)
    
    def __rmod__(self, obj):
        return self.__string.__rmod__(obj)
    
    def __rmul__(self, n):
        return self.__mul__(n)
    
    def capitalize(self):
        return self.__class__(
            self.indent + self.body.capitalize() + self.ending)
    
    def center(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.center(*args, **kwargs) + self.ending)
    
    def count(self, *args, **kwargs):
        return self.body.count(*args, **kwargs)
    
    def find(self, *args, **kwargs):
        return self.__string.find(*args, **kwargs)
    
    def index(self, *args, **kwargs):
        return self.__string.index(*args, **kwargs)
    
    def isalnum(self):
        return self.body.isalnum()
    
    def isalpha(self):
        return self.body.isalpha()
    
    def isdigit(self):
        return self.body.isdigit()
    
    def islower(self):
        return self.body.islower()
    
    def isupper(self):
        return self.body.isupper()
    
    def join(self, *args, **kwargs):
        return self.__string.join(*args, **kwargs)
    
    def rfind(self, *args, **kwargs):
        return self.__string.rfind(*args, **kwargs)
    
    def rindex(self, *args, **kwargs):
        return self.__string.rindex(*args, **kwargs)
    
    def decode(self, *args, **kwargs):
        return Line(self.__string.decode(*args, **kwargs),
            tab_width=self.tab_width)
    
    def encode(self, *args, **kwargs):
        return Line(self.__string.encode(*args, **kwargs),
            tab_width=self.tab_width)
    
    def endswith(self, *args, **kwargs):
        return self.body.endswith(*args, **kwargs)
    
    def expandtabs(self, tabsize=False):
        tab_width = tabsize or self.tab_width
        return self.__class__(
            self.indent.expandtabs(tab_width) + self.body + self.ending)
    
    def ljust(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.ljust(*args, **kwargs) + self.ending)
    
    def lower(self):
        return self.__class__(self.__string.lower())
    
    def lstrip(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.lstrip(*args, **kwargs) + self.ending)
    
    def partition(self, *args, **kwargs):
        return self.body.partition(*args, **kwargs)
    
    def replace(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.replace(*args, **kwargs) + self.ending)
    
    def rjust(self, *args, **kwargs):
        return self.__class__(
        self.body.rjust(*args, **kwargs) + self.ending)
    
    def rpartition(self, *args, **kwargs):
        return self.body.rpartition(*args, **kwargs)
    
    def rsplit(self, *args, **kwargs):
        return self.body.rsplit(*args, **kwargs)
    
    def rstrip(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.rstrip(*args, **kwargs) + self.ending)
    
    def split(self, *args, **kwargs):
        return self.body.split(*args, **kwargs)
    
    def splitlines(self, keepends=False):
        if keepends:
            return [self]
        return [self.__class__(self.indent + self.body)]
    
    def startswith(self, *args, **kwargs):
        return self.body.startswith(*args, **kwargs)
    
    def strip(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.strip(*args, **kwargs) + self.ending)
    
    def swapcase(self):
        return self.__class__(self.__string.swapcase())
    
    def title(self):
        return self.__class__(self.__string.title())
    
    def translate(self, *args, **kwargs):
        return self.__class__(
            self.indent + self.body.translate(*args, **kwargs) + self.ending)
    
    def upper(self, *args, **kwargs):
        return self.__class__(self.__string.upper())
    
    def zfill(self, width):
        return self.__class__(
        self.indent + self.body.zfill(width) + self.ending)
    
    body = property(__get_body, __set_body, __del_body)
    ending = property(__get_ending, __set_ending, __del_ending)
    indent = property(__get_indent, __set_indent, __del_indent)
    indent_type = property(__get_indent_type, __set_indent_type,
        __del_indent_type)


class UnicodeLine(SuperLine):
    
    string_class = unicode


class StrLine(SuperLine):
    
    string_class = str


def Line(string_in, tab_width=4):
    if isinstance(string_in, unicode):
        return UnicodeLine(string_in, tab_width=tab_width)
    return StrLine(string_in, tab_width=tab_width)


def unexpand(string, tab_width=4):
    if string.strip(' \t') != '':
        if '\t' in string or ' ' in string:
            strings = breakup(string, seps=' \t')
            return ''.join(unexpand(substr, tab_width=tab_width)
                for substr in strings)
        return string
    spaces = ' ' * tab_width
    if len(string.split('\t')) == 1:
        curr_string = ' ' * string.count(' ')
        string_out = ''
        while len(curr_string) >= tab_width:
            string_out += '\t'
            curr_string = curr_string[tab_width:]
        return string_out + curr_string
    return '\t'.join(map(unexpand, string.split('\t')))


def breakup(string, seps='\t\n\x0b\x0c\r '):
    broken, string = [string[0]], string[1:]
    for i, char in enumerate(string):
        if char in seps:
            if broken[-1][0] in seps:
                broken[-1] += char
            else:
                broken.append(char)
        else:
            if broken[-1][0] in seps:
                broken.append(char)
            else:
                broken[-1] += char
    return broken

STRING_TO_LINE = {
    unicode: UnicodeLine,
    str: StrLine,}
